package com.goo.utils;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @desc: jsch 调用远程服务器执行shell命令，并返回结果
 * @author: wangsh
 */
public class JSCHUtil {
    //链接超时时间
    private static int session_connect_timeout = 3000;
    //通道超时时间
    private static int channel_connect_timeout = 1000;
    //编码方式
    private static String charset = "UTF8";
    private static JSCHUtil instance;

    private JSCHUtil() {
    }

    public static JSCHUtil getInstance() {
        if (instance == null) {
            instance = new JSCHUtil();
        }
        return instance;
    }

    /**
     * 利用JSch包实现远程主机SHELL命令执行
     *
     * @param host     主机IP
     * @param port     主机ssh2登陆端口，如果取默认值，传-1
     * @param ueseName 主机登陆用户名
     */
    private static com.jcraft.jsch.Session getSession(String host, int port, String ueseName) throws Exception {
        System.out.println("建立session连接..........");
        JSch jsch = new JSch();
        com.jcraft.jsch.Session session = jsch.getSession(ueseName, host, port);

        return session;
    }

    /**
     * 利用JSch包实现远程主机SHELL命令执行
     *
     * @param host       主机IP
     * @param port       主机ssh2登陆端口，如果取默认值，传-1
     * @param ueseName   主机登陆用户名
     * @param password   主机登陆密码
     * @param privateKey 密钥文件路径
     * @param passphrase 密钥的密码
     */
    public static com.jcraft.jsch.Session connect(String host, int port, String ueseName, String password, String privateKey, String passphrase) throws Exception {
        JSch jsch = new JSch();
        //设置密钥和密码
        if (privateKey != null && !"".equals(privateKey)) {
            if (passphrase != null && "".equals(passphrase)) {
                //设置带口令的密钥
                jsch.addIdentity(privateKey, passphrase);
            } else {
                //设置不带口令的密钥
                jsch.addIdentity(privateKey);
            }
        }
        com.jcraft.jsch.Session session = jsch.getSession(ueseName, host, port);
        //设置登陆主机的密码
        session.setPassword(password);//设置密码

        //设置第一次登陆的时候提示，可选值：(ask | yes | no)
        java.util.Properties config = new java.util.Properties();
        config.setProperty("StrictHostKeyChecking", "no");
        session.setConfig(config);
        //设置登陆超时时间
        session.connect(session_connect_timeout);

        return session;
    }

    /**
     * 利用JSch包实现远程主机SHELL命令执行
     *
     * @param host     主机IP
     * @param port     主机ssh2登陆端口，如果取默认值，传-1
     * @param ueseName 主机登陆用户名
     * @param password 主机登陆密码
     */
    public static com.jcraft.jsch.Session connect(String host, int port, String ueseName, String password) throws Exception {
        com.jcraft.jsch.Session session = getSession(host, port, ueseName);
        session.setPassword(password);

        //设置第一次登陆的时候提示，可选值：(ask | yes | no)
        java.util.Properties config = new java.util.Properties();
        config.setProperty("StrictHostKeyChecking", "no");
        session.setConfig(config);
        //设置登陆超时时间
        session.connect(session_connect_timeout);

        return session;
    }

    /**
     * 执行命令
     *
     * @param session：链接
     * @param command：命令
     * @return
     * @throws Exception
     */
    public static String execCmd(com.jcraft.jsch.Session session, String command) throws Exception {

        if (session == null) {
            throw new RuntimeException("Session is null!");
        }

        ChannelExec exec = (ChannelExec) session.openChannel("exec");
        exec.setCommand(command); //执行命令
        exec.connect(channel_connect_timeout);//设置通道超时时间

        int exitStatus = exec.getExitStatus(); //退出状态为-1，直到通道关闭
        System.out.println("通道状态(退出状态为-1，直到通道关闭): " + exitStatus);

        //获取命令执行的结果
        InputStream instream = exec.getInputStream();
        StringBuffer buffer = read(instream);

        //关闭流
        instream.close();
        //关闭通道
        exec.disconnect();

        return buffer.toString();
    }

    /**
     * 执行命令
     *
     * @param session：链接
     * @param command：命令
     * @return
     * @throws Exception
     */
    public static String shellCmd(Session session, String command) throws Exception {
        //创建sftp通信通道
        Channel channel = session.openChannel("shell");
        channel.connect(channel_connect_timeout);
        //获取输出流d
        OutputStream outstream = channel.getOutputStream();
        //发送需要执行的SHELL命令，需要用\n结尾，表示回车
        String shellCommand = command + " \n";
        outstream.write(shellCommand.getBytes());
        outstream.flush();

        int exitStatus = channel.getExitStatus(); //退出状态为-1，直到通道关闭
        System.out.println("退出状态为-1，直到通道关闭: " + exitStatus);

        //获取命令执行的结果
        StringBuffer buffer = new StringBuffer();
//        buffer = getResult(instream);

        //获取输入流和输出流d
        InputStream instream = channel.getInputStream();
        //获取命令执行的结果
        buffer = getShellResult(instream);

        //关闭流
        outstream.close();
        instream.close();
        //关闭通道
        channel.disconnect();
        session.disconnect();

        return buffer.toString();
    }

    /**
     * 非阻塞读取执行结果
     *
     * @param instream
     * @return
     * @throws IOException
     */
    private static StringBuffer read(InputStream instream) throws IOException {
        //获取命令执行的结果
        StringBuffer buffer = new StringBuffer();
        byte[] buf = new byte[1024];
        //获取执行结果
        while (instream.read(buf) > 0) {
            String str = new String(buf, charset);
            buffer.append(str);
            System.out.println("获取执行结果： " + buffer);
        }
        System.out.println("执行命令结果： " + buffer);
        return buffer;
    }

    /**
     * 非阻塞读取执行结果
     *
     * @param instream
     * @return
     * @throws IOException
     */
    private static StringBuffer getShellResult(InputStream instream) throws IOException {
        StringBuffer buffer = new StringBuffer();
        //available：返回可读取（或跳过）的字节数的估计值，从这个输入流没有阻塞或
        int len = instream.available();
        while (len > 0) {
            byte[] data = new byte[len];
            int nLen = instream.read(data);
            if (nLen < 0) {
                System.out.println("读取shell数据内容为：" + nLen);
                continue;
            }
            //转换输出结果并打印出来
            // String temp = new String(data, 0, nLen,"UTF-8");
            String tempData = new String(data, charset);
            buffer.append(tempData);
            len = instream.available();
        }
        System.out.println("执行shell命令结果： " + buffer);
        return buffer;
    }

    /**
     * 关闭通道
     *
     * @param session
     */
    public void close(com.jcraft.jsch.Session session) {
        if ((session != null) && session.isConnected()) {
            session.disconnect();
            session = null;
        }
    }

    /**
     * 关闭通道
     *
     * @param channel
     */
    public void close(com.jcraft.jsch.Channel channel) {
        if ((channel != null) && channel.isConnected()) {
            channel.disconnect();
            channel = null;
        }
    }

    public static void main(String[] args) throws Exception {

        long begintime = System.currentTimeMillis();
//		 String cmd = "cd /" + ";" + "ls -al |grep home";
        String cmd = "cd /usr/local/" + ";" + "netstat -ntlp";
        try {
            //建立连接
            Session session = JSCHUtil.connect("8.135.42.12", 22, "root", "Dszn@2020");
            //执行命令
            String result = JSCHUtil.execCmd(session, cmd);
            //String result = JSCHUtil.shellCmd(session, cmd);
            System.out.println("执行shell命令结果：" + result);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        long endtinme = System.currentTimeMillis();
        System.out.println("总共用了" + (endtinme - begintime));
        System.exit(0);
    }
}
